package lol.clann.minecraft.springboot.plugin.service;

import com.github.benmanes.caffeine.cache.*;
import lol.clann.minecraft.springboot.api.model.CacheItem;
import lol.clann.minecraft.springboot.api.service.CacheService;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Service;

/**
 * 提供缓存机制
 *
 * 支持不同key不同的过期策略
 *
 * @author pyz
 * @date 2019/6/1 7:00 PM
 */
@Service
public class CacheServiceImpl implements CacheService {

    private final Cache<String, CacheItem> cache = Caffeine.newBuilder()
            .expireAfter(new Expiry<String, CacheItem>() {
                @Override
                public long expireAfterCreate(@NonNull String key, @NonNull CacheItem value, long currentTime) {
                    value.setCreated(currentTime);
                    return value.getExpireAfterCreate();
                }

                @Override
                public long expireAfterUpdate(@NonNull String key, @NonNull CacheItem value, long currentTime, @NonNegative long currentDuration) {
                    if (value.getExpireAfterUpdate() != -1) {
                        return value.getExpireAfterUpdate();
                    }
                    value.setLifeTime(value.getCreated() + value.getExpireAfterCreate() - currentTime);
                    return value.getLifeTime();
                }

                @Override
                public long expireAfterRead(@NonNull String key, @NonNull CacheItem value, long currentTime, @NonNegative long currentDuration) {
                    if (value.getExpireAfterRead() != -1) {
                        return value.getExpireAfterRead();
                    }
                    value.setLifeTime(value.getCreated() + value.getExpireAfterCreate() - currentTime);
                    return value.getLifeTime();
                }
            })
            .removalListener(new RemovalListener<String, CacheItem>() {
                @Override
                public void onRemoval(@Nullable String key, @Nullable CacheItem value, @NonNull RemovalCause cause) {
                    value.setLifeTime(0);
                }
            })
            .build();

    @Override
    public void invalidate(String key) {
        cache.invalidate(key);
    }

    @Override
    public <T> void putCacheItem(String key, CacheItem<T> item) {
        cache.put(key, item);
    }

    @Override
    public <T> CacheItem<T> getCacheItem(String key) {
        CacheItem<T> item = cache.getIfPresent(key);
        return item;
    }

    @Override
    public <T> void put(String key, T data) {
        CacheItem<T> item = new CacheItem<>();
        item.setData(data);
        cache.put(key, item);
    }

    @Override
    public <T> T get(String key) {
        CacheItem item = cache.getIfPresent(key);
        return item == null ? null : (T) item.getData();
    }

}
